import time
import threading
import multiprocessing

RUN_COUNT = 20000000
TIMES = 10
SINGLE_CONUT = int(RUN_COUNT / 10)

# 计数器单例对象，执行无锁
class CounterModel:
    '''
    计数器单例对象，执行无锁
    '''
    # 单例实例
    __instance = None
    __is__first = True
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self):
        if CounterModel.__is__first:
            CounterModel.__is__first = False
        else: 
            raise Exception("Cannot instantiate directly, use ApiModel.getInstance() instead")
    @staticmethod
    def getInstance():
        '''
        获取单例对象
        '''
        if not CounterModel.__instance:
            CounterModel.__instance = CounterModel()
        return CounterModel.__instance

    start_time :float = 0
    counter = RUN_COUNT
    def reduce(self):
        self.counter -= 1
        if self.counter == RUN_COUNT - SINGLE_CONUT:
            print(f"part time: {time.time() - self.start_time}")
        if self.counter == 0 :
            print(f">>> start_time:{self.start_time} cost time: {time.time() - self.start_time}")

# 计数器单例对象，执行有锁
class CounterWithLockModel:
    '''
    计数器单例对象，执行有锁
    '''
    # 单例实例
    __instance = None
    __is__first = True
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self):
        if CounterWithLockModel.__is__first:
            CounterWithLockModel.__is__first = False
        else: 
            raise Exception("Cannot instantiate directly, use ApiModel.getInstance() instead")
    @staticmethod
    def getInstance():
        '''
        获取单例对象
        '''
        if not CounterWithLockModel.__instance:
            CounterWithLockModel.__instance = CounterWithLockModel()
        return CounterWithLockModel.__instance

    lock = threading.Lock()
    start_time :float = 0
    counter = RUN_COUNT
    def reduce(self):
        self.lock.acquire()
        self.counter -= 1
        if self.counter == 0 :
            print(f">>> start_time:{self.start_time} cost time: {time.time() - self.start_time}")
        self.lock.release()

# 主线程执行
class OnMainTester:
    '''
    主线程执行
    '''
    @staticmethod
    def run():
        CounterModel.start_time = time.time()
        for i in range(RUN_COUNT):
            CounterModel.getInstance().reduce()

# 10个线程顺序执行
class TenThreadInOrderTester:
    '''
    10个线程顺序执行
    '''
    threadDict = {}
    def start(self):
        for i in range(SINGLE_CONUT):
            CounterModel.getInstance().reduce()

    def genThread(self, index):
        #创建线程
        thread = threading.Thread(target=self.start, name=f"Thread-{index}")
        self.threadDict[index] = thread
        
    def run(self):
        for i in range(TIMES):
            self.genThread(i)
        
        CounterModel.getInstance().start_time = time.time()
        for i in range(TIMES):
            t = self.threadDict[i]
            # t.setDaemon(True) # 守护线程，主线程一结束，就会随着一起销毁
            t.start() #执行线程
            t.join() #join() 等待线程执行完毕（也就是说卡在这里，这个线程执行完才会执行下一步）

# 10个线程顺序执行有锁任务
class TenThreadInOrderWithLockTester:
    '''
    10个线程顺序执行有锁任务
    '''
    threadDict = {}
    def start(self):
        for i in range(SINGLE_CONUT):
            CounterWithLockModel.getInstance().reduce()

    def genThread(self, index):
        #创建线程
        thread = threading.Thread(target=self.start, name=f"Thread-{index}")
        self.threadDict[index] = thread
        
    def run(self):
        for i in range(TIMES):
            self.genThread(i)
        
        CounterWithLockModel.getInstance().start_time = time.time()
        for i in range(TIMES):
            t = self.threadDict[i]
            # t.setDaemon(True) # 守护线程，主线程一结束，就会随着一起销毁
            t.start() #执行线程
            t.join() #join() 等待线程执行完毕（也就是说卡在这里，这个线程执行完才会执行下一步）

# 10个线程非顺序执行
class TenThreadNoOrderTester:
    '''
    10个线程非顺序执行
    '''
    threadDict = {}
    def start(self):
        for i in range(SINGLE_CONUT):
            CounterModel.getInstance().reduce()

    def genThread(self, index):
        thread = threading.Thread(target=self.start, name=f"Thread-{index}") #创建线程
        self.threadDict[index] = thread
        
    def run(self):
        for i in range(TIMES):
            self.genThread(i)
        
        CounterModel.getInstance().start_time = time.time()
        for i in range(TIMES):
            t = self.threadDict[i]
            t.start()
            # t.join() #join() 等待线程执行完毕（也就是说卡在这里，这个线程执行完才会执行下一步）

# 10个进程执行无锁任务
class TenProcessTester:
    '''
    10个进程
    '''
    pDict = {}
    def start(self):
        if CounterModel.getInstance().start_time == 0 :
            CounterModel.getInstance().start_time = time.time()

        for i in range(SINGLE_CONUT):
            CounterModel.getInstance().reduce()

    def genProcess(self, index):
        #创建进程
        p = multiprocessing.Process(target=self.start, name=f"Process-{index}")
        self.pDict[index] = p
        
    def run(self):
        for i in range(TIMES):
            self.genProcess(i)
        
        for i in range(TIMES):
            p = self.pDict[i]
            p.start() #开始进程



if __name__ == '__main__':
    print('==== main start')
    print(threading.current_thread().name) # 当前线程名称
    print(threading.current_thread().is_alive()) # 当前线程状态
    print(threading.current_thread().ident) # 当前线程的编号

    # 20000000  avg(5)
    # OnMainTester.run() # 3.33506
    # TenThreadInOrderTester().run() # 3.32512
    # TenThreadInOrderWithLockTester().run() # 4.72121
    # TenThreadNoOrderTester().run() # 3.45945
    # TenProcessTester().run() # 0.625836

    print('==== main stop')